#include "Logging.h"
#include "Console.h"
#include "string.h"

namespace {
    const CHAR* systemNameArray[] = {"|Other|",
                                     "|Memory|",
                                     "|Settings|",
                                     "|Containers|",
                                     "|FileSystem|",
                                     "|Strings|"};
    static CHAR* string = NULL;
}

namespace CEngine
{

Logging::Logging(const U8 v, const bool tc, FilePath* path, const U32 cpl)
    :   toConsole(tc),
        verbosity(v),
        charactersPerLine(cpl),
        totalLinesPrinted(0)
{
    if(path) {
        file = File(*path, CE_FILE_OPEN_ALWAYS | CE_FILE_ACCESS_WRITE | CE_FILE_ASCII);
        toFile = true;
        if(!file.open()) {
            toFile = false;
            Console::print("%qCould not create \"%s\" log file!\n%r", Console::LWHITE + (Console::LRED * 16), file.getPath().getFullPath());
        }
    }
    else {
        toFile = false;
    }
}

Logging::Logging(const U8 v, const bool tc, char* path, const U32 cpl)
    :   toConsole(tc),
        verbosity(v),
        charactersPerLine(cpl),
        totalLinesPrinted(0)
{
    if(path) {
        string = path;
        toFile = true;
    }
    else {
        toFile = false;
    }
}

void
Logging::log(const System system, const U8 v, const char* input, ...) {
    if(v > verbosity)
        return;
    if(string) {
        file = File(FilePath(string, false), CE_FILE_OPEN_ALWAYS | CE_FILE_ACCESS_WRITE | CE_FILE_ASCII);
        string = NULL;
        if(!file.open()) {
            toFile = false;
            Console::print("%qCould not create \"%s\" log file!\n%r", Console::LWHITE + (Console::LRED * 16), file.getPath().getFullPath());
        }
    }

    CHAR* buffer = new CHAR[charactersPerLine + 2];
    U32 bufferPosition = 0;
    U32 lastSpace = 0;

    va_list args;
    va_start(args, input);
    U32 totalStringLength = 0;

    CHAR formattedInput[(U32)1100000];
    U32 charactersFormatted = 0;
    charactersFormatted = Console::vsPrint(formattedInput, (U32)1100000, input, args);

    Console::TextColour foreground, background = Console::BLACK;
    CHAR* name = (CHAR*)systemNameArray[system];
    CHAR* extra = "";
    switch(system) {
        case MEMORY:    foreground = Console::GREEN;
            break;
        case SETTINGS:  foreground = Console::LAQUA;
            break;
        case CONTAINERS:foreground = Console::LGREEN;
            break;
        case FILESYSTEM:foreground = Console::BLUE;
            background = Console::GRAY;
            break;
        case STRINGS:   foreground = Console::LYELLOW;
            break;
        default:        foreground = Console::LWHITE;
    }

    switch(v) {
        case LOG_ERROR:     background = Console::LRED;
                            extra = "[ERROR]";
                            break;
        case LOG_WARNING:   background = Console::LYELLOW;
                            extra = "[WARNING]";
                            break;
    }

    if(toFile) {
        CHAR temp[20 + 9 + 2];
        I32 length = Console::sPrint(temp, sizeof(temp), "%s%s%c ", name, extra, ':');
        file.write((U8*)temp, length, true);
    }
    if(toConsole) {
        Console::setTextColour(foreground, background);
    }

    for (U64 i = 0; formattedInput[i] != '\0' ; ++i) {
        if(bufferPosition == 0) {
            buffer[bufferPosition] = formattedInput[i];
            ++bufferPosition;
            continue;
        }
        if(bufferPosition <= charactersPerLine && buffer[bufferPosition - 1] == ' ') {
            lastSpace = bufferPosition - 1;
        }
        if(buffer[bufferPosition - 1] == '\n' && bufferPosition <= charactersPerLine - 1) {
            buffer[bufferPosition] = '\0';

            if(toConsole) {
                Console::vPrint(buffer, args);
            }
            if(toFile) {
                CHAR temp[charactersPerLine + 1];
                I32 length = Console::sPrint(temp, charactersPerLine + 1, "%s", buffer);
                file.write((U8*)temp, length, true);
            }

            bufferPosition = 0;
            lastSpace = 0;
            totalStringLength = 0;
            ++totalLinesPrinted;
            --i;
        }
        else if(bufferPosition + totalStringLength >= charactersPerLine) {
            if(lastSpace <= bufferPosition - 20) {
                buffer[bufferPosition - 2] = '\n';
                buffer[bufferPosition - 1] = '\0';

                if(toConsole) {
                    Console::vPrint(buffer, args);
                }
                if(toFile) {
                    CHAR temp[charactersPerLine + 1];
                    I32 length = Console::sPrint(temp, charactersPerLine + 1, "%s", buffer);
                    file.write((U8*)temp, length, true);
                }

                bufferPosition = 0;
                lastSpace = 0;
                totalStringLength = 0;
                -- --i;
                ++totalLinesPrinted;
            }
            else {
                buffer[lastSpace] = '\n';
                buffer[lastSpace + 1] = '\0';

                if(toConsole) {
                    Console::vPrint(buffer, args);
                }

                if(toFile) {
                    CHAR temp[charactersPerLine + 1];
                    I32 length = Console::sPrint(temp, charactersPerLine + 1, "%s", buffer);
                    file.write((U8*)temp, length, true);
                }

                i -= bufferPosition - lastSpace;
                bufferPosition = 0;
                lastSpace = 0;
                totalStringLength = 0;
                ++totalLinesPrinted;
            }
        }
        else {
            buffer[bufferPosition] = formattedInput[i];
            ++bufferPosition;
        }
    }
    if(bufferPosition > 0) {
        if(buffer[bufferPosition - 1] != '\n' ) {
            buffer[bufferPosition] = '\n';
            buffer[bufferPosition + 1] = '\0';

            if(toConsole) {
                Console::vPrint(buffer, args);
            }

            if(toFile) {
                CHAR* temp = new CHAR[charactersPerLine + 1];
                I32 length = Console::sPrint(temp, charactersPerLine + 1, "%s", buffer);
                file.write((U8*)temp, length, true);
                delete temp;
            }

            ++totalLinesPrinted;
        }
        else {
            buffer[bufferPosition] = '\0';

            if(toConsole) {
                Console::vPrint(buffer, args);
            }

            if(toFile) {
                CHAR temp[charactersPerLine + 1];
                I32 length = Console::sPrint(temp, charactersPerLine + 1, "%s", buffer);
                file.write((U8*)temp, length, true);
            }

            ++totalLinesPrinted;
        }
    }


    va_end(args);

    Console::setTextColour(Console::RESET, Console::RESET);

    if(buffer) {
        delete buffer;
    }

    if(charactersFormatted >= 1024) {
        //log(NONE, LOG_WARNING, "Log input was greater than 1023 characters.\n");
    }
}

}
